package com.ponhu.ea2.stockcenter.provider.service.biz.stocksales.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.ponhu.ea2.stockcenter.dto.OrderInfoDTO;
import com.ponhu.ea2.stockcenter.dto.OrderInventoryInfo;
import com.ponhu.ea2.stockcenter.enums.DefinedSystem;
import com.ponhu.ea2.stockcenter.enums.ExchangeActionTypeEnum;
import com.ponhu.ea2.stockcenter.enums.ResponseCodeEnum;
import com.ponhu.ea2.stockcenter.provider.constants.CommonConstant;
import com.ponhu.ea2.stockcenter.provider.entity.StockInventoryDocOccupy;
import com.ponhu.ea2.stockcenter.provider.service.biz.stocksales.SalesLevelStockResponseService;
import com.ponhu.ea2.stockcenter.provider.service.biz.stocksales.SalesStockChangeCoreHandler;
import com.ponhu.ea2.stockcenter.provider.service.entity.StockInventoryDocOccupyService;
import com.ponhu.ea2.stockcenter.provider.support.enums.StockChangeInnerEventEnum;
import com.ponhu.ea2.stockcenter.provider.support.exception.StockRuntimeException;
import com.ponhu.ea2.stockcenter.provider.support.service.RedisSupportService;
import com.ponhu.ea2.stockcenter.provider.support.vo.InventoryInfoVO;
import com.ponhu.ea2.stockcenter.provider.utils.RedisUtils;
import com.ponhu.ea2.stockcenter.vo.OrderInventoryVO;
import com.ponhu.ea2.stockcenter.vo.OrderOccupyInfoVO;
import com.ponhu.ea2.stockcenter.vo.ReturnResponseBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.ponhu.ea2.stockcenter.enums.ResponseCodeEnum.ADD_OCCUPY_STOCK_EXCEPTION_900201;
import static com.ponhu.ea2.stockcenter.enums.ResponseCodeEnum.DOC_NO_IS_EXISTS_EXCEPTION_900200;

/**
 * 销售层-库存响应服务
 */
@Slf4j
@Service
public class SalesLevelStockResponseServiceImpl implements SalesLevelStockResponseService {

    @Autowired
    private StockInventoryDocOccupyService stockInventoryDocOccupyService;

    @Autowired
    private SalesStockChangeCoreHandler SalesStockChangeCoreHandler;

    @Autowired
    private RedisSupportService redisSupportService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ReturnResponseBean<OrderOccupyInfoVO> orderOccupyStockHandler(String stockRequestToken,
                                                                         OrderInfoDTO orderInfoDTO,
                                                                         DefinedSystem definedSystem,
                                                                         StockChangeInnerEventEnum stockChangeInnerEventEnum) {
        //参数检查
        boolean checkResult = checkParamsIsCorrect(orderInfoDTO);
        if (!checkResult) {
            log.error("销售层占用库存失败：参数不正确");
            return ReturnResponseBean.error(ResponseCodeEnum.PARAMS_INCORRECT_EXCEPTION_900003);
        }

        //验证单据号是否已经存在
        checkDocNoIsExists(orderInfoDTO.getDocNo());

        List<OrderInventoryInfo> inventoryList = new ArrayList<>();
        List<String> lockKeyList = orderInfoDTO.getOrderInventoryInfoList().stream().map(i ->
        {
            inventoryList.add(new OrderInventoryInfo()
                    .setInventoryCode(i.getInventoryCode())
                    //销售下单，可售--，设置数量为负数
                    .setInventoryQuantity(-i.getInventoryQuantity()));
            return RedisUtils.getRedisKey(CommonConstant.REDISSON_RW_LOCK_STOCK_GLOBAL, i.getInventoryCode());
        }).collect(Collectors.toList());

        return redisSupportService.wrapWithWriteLock(lockKeyList, () -> {
            //处理单据占用事件，更新占用库存
            boolean result = this.handleSalesLevelChangeEvent(orderInfoDTO.getDocNo(), definedSystem,
                    stockChangeInnerEventEnum, inventoryList);

            if (result) {
                log.info("销售层处理单据占用事件成功 ===> 单据号：{}", orderInfoDTO.getDocNo());
                //添加单据占用记录
                return addSalesOrderOccupyStockRecord(orderInfoDTO, definedSystem, stockChangeInnerEventEnum);
            } else {
                log.error("销售层处理单据占用事件失败 ===> 参数：{}", JSONObject.toJSONString(orderInfoDTO));
                return ReturnResponseBean.error(ADD_OCCUPY_STOCK_EXCEPTION_900201);
            }
        });
    }

    private void checkDocNoIsExists(String docNo) {
        //如果已经存在"已下单"状态占用记录，那么不可重复下单占用库存，抛异常
        List<StockInventoryDocOccupy> docOccupyList = stockInventoryDocOccupyService.getPlacedDocRecordList(docNo);
        if(CollectionUtils.isNotEmpty(docOccupyList)){
            throw new StockRuntimeException(DOC_NO_IS_EXISTS_EXCEPTION_900200);
        }
    }

    @Override
    public ReturnResponseBean cancelOrderOccupyStockHandler(String stockRequestToken,
                                                            String orderNo,
                                                            DefinedSystem definedSystem,
                                                            StockChangeInnerEventEnum stockChangeInnerEventEnum) {
        if (orderNo == null) {
            return ReturnResponseBean.error(ResponseCodeEnum.PARAMS_INCORRECT_EXCEPTION_900003);
        }

        //1. 取消单据占用
        //根据订单号查询所有单据列表
        List<StockInventoryDocOccupy> docList = stockInventoryDocOccupyService.getDocRecordList(orderNo);

        //单据不存在，打回
        if (CollectionUtils.isEmpty(docList)) {
            return ReturnResponseBean.error(ResponseCodeEnum.DOC_NOT_FOUND_EXCEPTION_900212);
        }

        //单据已取消，打回
        List<StockInventoryDocOccupy> canceledDocList = docList.stream().filter(f -> f.getStatus() == CommonConstant.DOC_OCCUPY_STATUS.CANCELED).collect(Collectors.toList());
        List<StockInventoryDocOccupy> orderedDocList = docList.stream().filter(f -> f.getStatus() == CommonConstant.DOC_OCCUPY_STATUS.PLACED).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(canceledDocList) && CollectionUtils.isEmpty(orderedDocList)) {
            return ReturnResponseBean.error(ResponseCodeEnum.DOC_CANCELED_EXCEPTION_900213);
        }


        //单据已出库，打回
        List<StockInventoryDocOccupy> outedDocList = docList.stream().filter(f -> f.getStatus() == CommonConstant.DOC_OCCUPY_STATUS.OUTED).collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(outedDocList) && CollectionUtils.isEmpty(orderedDocList)){
            return ReturnResponseBean.error(ResponseCodeEnum.DOC_OUTED_EXCEPTION_900214);
        }

        //获取key值
        List<String> lockKeyList = orderedDocList.stream().map(i ->
                RedisUtils.getRedisKey(CommonConstant.REDISSON_RW_LOCK_STOCK_GLOBAL, i.getInventoryCode())).collect(Collectors.toList());

        return redisSupportService.wrapWithWriteLock(lockKeyList, () -> {
            if (orderedDocList.get(0).getStatus() == CommonConstant.DOC_OCCUPY_STATUS.PLACED &&
                    orderedDocList.get(0).getIsUsed() &&
                    orderedDocList.get(0).getDocTransferStatus() == CommonConstant.DOC_TRANSFER_STATUS.NO_TRANSFER_APPLY_AND_NO_OUT) {
                //只有已下单、已占用、未调拨申请未出可售仓的订单需要"释放库存"
                List<OrderInventoryInfo> orderInventoryInfoList = orderedDocList.stream().map(i -> {
                    OrderInventoryInfo orderInventoryInfo = new OrderInventoryInfo();
                    return orderInventoryInfo.setInventoryCode(i.getInventoryCode())
                            .setInventoryQuantity(i.getUsedNum());
                }).collect(Collectors.toList());

                //处理取消单据事件
                this.handleSalesLevelChangeEvent(orderNo, definedSystem, stockChangeInnerEventEnum, orderInventoryInfoList);
            }

            //2. 更新单据占用表状态
            boolean result = stockInventoryDocOccupyService.cancelDocOccupy(orderNo, stockChangeInnerEventEnum);

            if (result) {
                log.info("更新订单状态成功,单据号：{}", orderNo);
                return ReturnResponseBean.ok();
            } else {
                log.error("更新订单状态失败,单据号：{}", orderNo);
                return ReturnResponseBean.error(ResponseCodeEnum.DOC_STATUS_UPDATE_EXCEPTION_900215);
            }
        });
    }


    private boolean handleSalesLevelChangeEvent(String orderNo, DefinedSystem definedSystem,
                                                StockChangeInnerEventEnum changeInnerEventEnum,
                                                List<OrderInventoryInfo> orderInventoryInfoList) {
        //组装数据
        List<InventoryInfoVO> inventoryInfoList = orderInventoryInfoList.stream().map(i -> {
            InventoryInfoVO inventoryInfo = new InventoryInfoVO();
            BeanUtils.copyProperties(i, inventoryInfo);

            return inventoryInfo;
        }).collect(Collectors.toList());

        //处理库存变动内部事件
        return SalesStockChangeCoreHandler.handleStockChangeInnerEvent(orderNo, definedSystem, changeInnerEventEnum, inventoryInfoList);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean handleRealityLevelChangeEvent(String docNo, StockChangeInnerEventEnum changeEventEnum, List<InventoryInfoVO> inventoryInfoList) {
        ////处理库存变动内部事件
        return SalesStockChangeCoreHandler.handleStockChangeInnerEvent(docNo, DefinedSystem.WMS, changeEventEnum, inventoryInfoList);
    }

    private ReturnResponseBean<OrderOccupyInfoVO> addSalesOrderOccupyStockRecord(OrderInfoDTO orderInfoDTO, DefinedSystem definedSystem, StockChangeInnerEventEnum stockChangeInnerEventEnum) {

        stockInventoryDocOccupyService.addSalesOrderOccupyStockRecord(orderInfoDTO, definedSystem, stockChangeInnerEventEnum);

        List<OrderInventoryVO> orderInventoryVOList = orderInfoDTO.getOrderInventoryInfoList().stream().map(i -> {
            OrderInventoryVO inventoryVO = new OrderInventoryVO();
            inventoryVO.setInventoryCode(i.getInventoryCode());
            inventoryVO.setOccupyQuantity(i.getInventoryQuantity());

            return inventoryVO;
        }).collect(Collectors.toList());

        log.info("销售层添加占用记录成功 ===> 单据号：{}", orderInfoDTO.getDocNo());
        return ReturnResponseBean.ok(new OrderOccupyInfoVO().setOrderNo(orderInfoDTO.getDocNo())
                .setOrderInventoryVOList(orderInventoryVOList));
    }

    private boolean checkParamsIsCorrect(OrderInfoDTO orderInfoDTO) {
        //单据参数校验
        if (orderInfoDTO == null || StringUtils.isEmpty(orderInfoDTO.getDocNo())
                || CollectionUtils.isEmpty(orderInfoDTO.getOrderInventoryInfoList())) {
            return false;
        }

        for (OrderInventoryInfo i : orderInfoDTO.getOrderInventoryInfoList()) {
            if (i == null || i.getInventoryCode() == null || i.getInventoryQuantity() == null || i.getInventoryQuantity() <= 0) {
                return false;
            }
        }

        return true;
    }
}
